#include "serial.h"
#include<QDataStream>

Serial* Serial::mPtr = nullptr;

Serial *Serial::instance()
{
    if(nullptr == mPtr)
        mPtr = new Serial();
    return mPtr;
}

Serial::Serial(QObject *parent) : QObject(parent)
{
    serial = new QSerialPort;
    connect(serial, &QSerialPort::readyRead, this, [=]()
    {
        QByteArray data = serial->readAll();
        emit haveReceive(data);
    });
}

QStringList Serial::findPort()
{
    QStringList portBuff;
    foreach (const QSerialPortInfo &info,QSerialPortInfo::availablePorts())
    {
        QSerialPort serial;
        serial.setPort(info);
        if(serial.open(QIODevice::ReadWrite))
        {
            portBuff.append(serial.portName());
            serial.close();
        }
    }
    return portBuff;
}
QStringList Serial::findBaudRate()
{
    QStringList baudList;
    baudList << QString::number(1200);
    baudList << QString::number(4800);
    baudList << QString::number(9600);
    baudList << QString::number(19200);
    baudList << QString::number(38400);
    baudList << QString::number(57600);
    baudList << QString::number(115200);
    return baudList;
}

bool Serial::open(QString port,quint64 baud)
{
    if(serial->isOpen())    serial->close();
    serial->setPortName(port);
    if(!serial->open(QIODevice::ReadWrite))
        return false;
    switch (baud)
    {
        case 1200: serial->setBaudRate(QSerialPort::Baud1200);   break;
        case 4800: serial->setBaudRate(QSerialPort::Baud4800);   break;
        case 9600: serial->setBaudRate(QSerialPort::Baud9600);   break;
        case 19200: serial->setBaudRate(QSerialPort::Baud19200);  break;
        case 38400: serial->setBaudRate(QSerialPort::Baud38400);  break;
        case 57600: serial->setBaudRate(QSerialPort::Baud57600);  break;
        case 115200: serial->setBaudRate(QSerialPort::Baud115200); break;
    }
    switch (8){
        case 5:     serial->setDataBits(QSerialPort::Data5);     break;
        case 6:     serial->setDataBits(QSerialPort::Data6);     break;
        case 7:     serial->setDataBits(QSerialPort::Data7);     break;
        case 8:     serial->setDataBits(QSerialPort::Data8);     break;
        default:     break;
    }
    switch (0){
        case 0:     serial->setParity(QSerialPort::NoParity);   break;
        default:    break;
    }
    switch (1){
        case 1: serial->setStopBits(QSerialPort::OneStop);  break;
        case 2: serial->setStopBits(QSerialPort::TwoStop);  break;
        default:     break;
    }
    serial->setFlowControl(QSerialPort::NoFlowControl);
    return true;
}

qint64 Serial::sendData(QByteArray data)
{
    bool isOk = 0;
    if(serial->write(data) > 0)
        isOk = 1;
    QString dataStr = HexToHexstr(data);
    //QString dataStr = "Tx(hex):" + HexToHexstr(data);
    emit sendDataShow(dataStr,isOk);
    return isOk;
}

qint64 Serial::sendData(QString data)
{
    bool isOk = 0;
    if(serial->write(data.toLatin1()) > 0)
        isOk = 1;
    //data.insert(0,"Tx:");
    emit sendDataShow(data,isOk);
    return isOk;
}


/*******************************************
时间：2019-4-22 10点42分
作者：Kiang Tao
功能：HEX数组转HEX字符串
输入参数：hex数组  如：01 ff 0f f0
返回值：hex字符串  如："01 ff 0f f0"
*******************************************/
QString Serial::HexToHexstr(const QByteArray hex)
{
    //QString::number(hex,16);
    if(hex.isEmpty())   return "";
    QByteArray DataIn = hex;
    QString HexStr;
    QDataStream DataStream(&DataIn,QIODevice::ReadWrite);
    while(!DataStream.atEnd())
    {
        quint8 outChar = 0;
        DataStream >> outChar;
        HexStr += QString("%1").arg(outChar & 0xff,2,16,QLatin1Char('0'))+" ";
    }
    return HexStr.toUpper();    //换大写字母
}
/*******************************************
时间：2019-4-22 14点01分
作者：Kiang Tao
功能：HEX字符串转HEX数组
输入参数：hex字符串    如："01 ff 0f f0"
返回值：hex数组       如：01 ff 0f f0
*******************************************/
QByteArray Serial::HexstrToByte(QString str)
{
    QByteArray byte_arr;
    QString prote_string = str;
    int len;
    bool ok;    //如果str的长度 不是2的倍数  那么直接返回空
    prote_string.replace(QString("\n"), QString(" "));//把空格挺换为什么都没有 去掉空格
    prote_string.replace(QString(" "), QString(""));//把空格挺换为什么都没有 去掉空格
    if( prote_string.startsWith("0x")||prote_string.startsWith("0X"))        //使用的0x格式16进制
    {
        if(prote_string.size()%4!=0)    //如果字符串不是4的倍数0xff 0x00 排除不标准格式
            return QByteArray::fromHex("字符串不符合标准");
        len=prote_string.size()/4;
        for(int i=0;i<len;i++)
            byte_arr.append(char(prote_string.mid(i*4+2,2).toUShort(&ok,16)));  //mid(i,2)i为位置，2为长度
    }
    else
    {
        if(prote_string.size()%2!=0)    //如果字符串不是成双
            return QByteArray::fromHex("字符串不符合标准");
        len=prote_string.size()/2;
        for(int i=0;i<len;i++)
        {
            byte_arr.append(char(prote_string.mid(i*2,2).toUShort(&ok,16)));  //mid(i,2)i为位置，2为长度
        }
    }
    return byte_arr;
}

/*-------------------------------------------------------
**函数名: CRC_16(unsigned char data1[],unsigned char Length)
**功 能: CRC_16 校验计算子程序
**传 入:data1[] 待校验的数组
                 Length 转换的数组长度
**传 出: 16 位校验结果
--------------------------------------------------------*/
unsigned int Serial::CRC_16(unsigned char data1[],unsigned char Length)
{
    unsigned char CRC16Lo,CRC16Hi; //CRC 寄存器
    unsigned char CL,CH; //多项式码&HA001
    unsigned char SaveHi,SaveLo; //CRC 数据暂存
    unsigned char i; //循环变量
    unsigned int Flag; //循环变量
    CRC16Lo = 0xFF;//校验码寄存器初值
    CRC16Hi = 0xFF;
    CL = 0x01;//多项式码初值
    CH = 0xA0;
    for (i = 0; i < Length; i++)
    {
        CRC16Lo = (unsigned char)(CRC16Lo ^ data1[i]);//每一个数据与 CRC 寄存器进行异或
        for (Flag = 0; Flag <=7 ; Flag++)
        {
            SaveHi = CRC16Hi;
            SaveLo = CRC16Lo;
            CRC16Hi = (unsigned char)(CRC16Hi >> 1); //高位右移一位
            CRC16Lo = (unsigned char)(CRC16Lo >> 1); //低位右移一位
            if ((SaveHi & 0x01) == 0x01) //如果高位字节最后一位为 1
            {
                CRC16Lo = (unsigned char)(CRC16Lo | 0x80); //则低位字节右移后前面补 1
            } //否则自动补 0
            if ((SaveLo & 0x01) == 0x01) //如果 LSB 为 1，则与多项式码进行异或
            {
                CRC16Hi = (unsigned char)(CRC16Hi ^ CH);
                CRC16Lo = (unsigned char)(CRC16Lo ^ CL);
            }
        }
    }
    return ((CRC16Hi<<8) | CRC16Lo);
}

QString Serial::strDec2Hex(QString decStr)
{
    int data = decStr.toInt();
    return QString::number(data,16);
}

QString Serial::strHex2Dec(QString hexStr)
{
    QByteArray dataByte = hexStr.toLocal8Bit();
    bool isOk;
    int data = dataByte.toInt(&isOk,16);
    return QString::number(data);
}

